package com.raft.core.node;

import javax.annotation.Nonnull;

import com.raft.core.log.statemachine.StateMachine;
import com.raft.core.node.role.RoleNameAndLeaderId;
import com.raft.core.node.task.GroupConfigChangeTaskReference;

/**核心组件<br/>
 * 是暴露给上层服务的接口
 */
public interface Node {

    /**注册状态机
     * <br/>在服务启动时调用，上层服务将自己注册到核心组件中，核心组件负责在写入日志到文件后回调上层服务
     * Register state machine to node.
     * <p>State machine should be registered before node start, or it may not take effect.</p>
     *
     * @param stateMachine state machine
     */
    void registerStateMachine(@Nonnull StateMachine stateMachine);

    /**
     * Get current role name and leader id.
     * <p>
     * Available results:
     * </p>
     * <ul>
     * <li>FOLLOWER, current leader id</li>
     * <li>CANDIDATE, <code>null</code></li>
     * <li>LEADER, self id</li>
     * </ul>
     *
     * @return role name and leader id
     */
    @Nonnull
    RoleNameAndLeaderId getRoleNameAndLeaderId();

    /**
     * Add node role listener.
     *
     * @param listener listener
     */
    void addNodeRoleListener(@Nonnull NodeRoleListener listener);

    /**启动，在服务启动时调用
     */
    void start();

    /**追加日志<br/>
     * 真正参与命令执行的方法，上层服务提供二进制数组格式的命令内容后，核心组件负责追加日志、节点间的复制等操作
     *
     * @param commandBytes command bytes
     * @throws NotLeaderException if not leader
     */
    void appendLog(@Nonnull byte[] commandBytes);

    /**对于操作方来说，节点被安全加入集群的时刻在日志被提交之后。在那之前，包含新集群配置的日志有可能被其他节点覆盖，所以完成被设置在日志提交之后，增加节点的操作也在日志被提交之后返回
     * <br/>Add node.
     *
     * @param endpoint new node endpoint
     * @return task reference
     * @throws NotLeaderException if not leader
     * @throws IllegalStateException if group config change concurrently
     */
    @Nonnull
    GroupConfigChangeTaskReference addNode(@Nonnull NodeEndpoint endpoint);

    /**移除节点，其主要内容是等待前一个任务，然后新建任务并提交
     * <br/>Remove node.
     *
     * @param id id
     * @return task reference
     * @throws NotLeaderException if not leader
     * @throws IllegalStateException if group config change concurrently
     */
    @Nonnull
    GroupConfigChangeTaskReference removeNode(@Nonnull NodeId id);

    /**关闭，在服务关闭时调用
     *
     * @throws InterruptedException if interrupted
     */
    void stop() throws InterruptedException;

	/**把当前请求加入ReadIndex队列
	 * @param requestId
	 */
	void enqueueReadIndex(String requestId);

}
